﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using AMM.Model;
using AMM.Model.Store;

namespace AMM.DataAccess.Store {
    public class SuspenseOrderRepository : BaseRepository<SuspenseOrder> {

        private int _pageSize;
        private ProcurementOrderRepository _procurementOrderRepository;

        public SuspenseOrderRepository(int pageSize = int.MaxValue) {
            this._pageSize = pageSize;
            this._procurementOrderRepository = new ProcurementOrderRepository(int.MaxValue);
        }

        public SuspenseOrder FetchSuspenseOrderById(int suspenseOrderId) {
            using (var ctx = this.Context) {
                return ctx.SuspenseOrders
                    .Include("SuspenseOrderDetails")
                    .FirstOrDefault(x => x.SuspenseOrderId == suspenseOrderId);
            }
        }

        public SuspenseOrder FetchSuspenseOrderByIdForView(int suspenseOrderId) {
            using (var ctx = this.Context) {
                return ctx.SuspenseOrders
                    .Include("SuspenseOrderDetails")
                    .Include("SuspenseOrderDetails.Material")
                    .Include("SuspenseOrderDetails.Material.Basic1")
                    .FirstOrDefault(x => x.SuspenseOrderId == suspenseOrderId);
            }
        }

        public List<SuspenseOrderDetail> FetchMaterialViewModelListBySuspenseOrderId(int suspenseOrderId) {
            using (var ctx = this.Context) {
                return ctx.SuspenseOrderDetails
                    .Include("Material")
                    .Include("Material.Basic")
                    .Where(x => x.SuspenseOrderId == suspenseOrderId).ToList();
            }
        }

        public void UpdateSuspenseOrder(SuspenseOrder suspenseOrder) {
            using (var ctx = this.Context) {
                using (DbContextTransaction trans = ctx.Database.BeginTransaction()) {
                    try {
                        suspenseOrder.SuspenseOrderDetails.Where(x => x.ClassStatus == ClassStatus.Added).ToList().ForEach(x => {
                            x.SuspenseOrder = suspenseOrder;
                            x.SuspenseOrderId = suspenseOrder.SuspenseOrderId;
                            x.SuspenseOrderDetailsId = Guid.NewGuid().GetHashCode();
                        });
                        // do not try to merge with above codes, because you can't change the fields which was marked as primary key
                        suspenseOrder.SuspenseOrderDetails.ToList().ForEach(x => {
                            ctx.SuspenseOrderDetails.Attach(x);
                            switch (x.ClassStatus) {
                                case ClassStatus.UnChanged:
                                    ctx.SuspenseOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Unchanged;
                                    break;
                                case ClassStatus.Updated:
                                    x.ModifiedBy = suspenseOrder.ModifiedBy;
                                    x.ModifiedDatetime = DateTime.Now;
                                    ctx.SuspenseOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Modified;
                                    break;
                                case ClassStatus.Added:
                                    x.CreatedBy = suspenseOrder.ModifiedBy;
                                    x.CreatedDatetime = DateTime.Now;
                                    ctx.SuspenseOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Added;
                                    break;
                                case ClassStatus.Deleted:
                                    ctx.SuspenseOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Deleted;
                                    break;
                            }
                        });

                        ctx.SuspenseOrders.Attach(suspenseOrder);
                        ctx.Entry(suspenseOrder).State = EntityState.Modified;

                        ctx.SaveChanges();
                        trans.Commit();
                    }
                    catch (Exception ex) {
                        trans.Rollback();
                        throw ex;
                    }
                }
            }
        }

        public void DeleteMultipleSuspenseOrder(params int[] suspenseOrderIds) {
            using (var ctx = this.Context) {
                using (DbContextTransaction trans = ctx.Database.BeginTransaction()) {
                    try {
                        ctx.SuspenseOrderDetails.Where(x => suspenseOrderIds.Contains(x.SuspenseOrderId)).ToList().ForEach(x => {
                            ctx.SuspenseOrderDetails.Remove(x);
                        });
                        ctx.SuspenseOrders.Where(x => suspenseOrderIds.Contains(x.SuspenseOrderId)).ToList().ForEach(x => {
                            ctx.SuspenseOrders.Remove(x);
                        });
                        ctx.SaveChanges();
                        trans.Commit();
                    }
                    catch (Exception ex) {
                        trans.Rollback();
                        throw ex;
                    }
                }
            }
        }

        public List<SuspenseOrder> FetchSuspenseOrderList(SuspenseOrderSearchModel searchModel, int pageIndex) {
            using (var ctx = this.Context) {
                var query = ctx.SuspenseOrders
                    .Include("ProcurementOrder")
                    .Include("SuspenseOrderDetails")
                    .Include("SuspenseOrderDetails.Material")
                    .Where(x => !string.IsNullOrEmpty(x.Code));

                if (!string.IsNullOrEmpty(searchModel.SuspenseOrderCode)) {
                    query = query.Where(x => x.Code.Contains(searchModel.SuspenseOrderCode));
                }
                if (!string.IsNullOrEmpty(searchModel.ProcurementOrderCode)) {
                    query = query.Where(x => x.ProcurementOrder.Code.Contains(searchModel.ProcurementOrderCode));
                }
                if (!string.IsNullOrEmpty(searchModel.DeliveryOrderCode)) {
                    query = query.Where(x => x.DeliveryOrderCode.Contains(searchModel.DeliveryOrderCode));
                }
                if (!string.IsNullOrEmpty(searchModel.MaterialCode)) {
                    query = query.Where(x => x.SuspenseOrderDetails.Any(y => y.Material.Code.Contains(searchModel.MaterialCode)));
                }
                if (searchModel.SuspenseDateStart.HasValue) {
                    query = query.Where(x => x.SuspenseOrderDetails.Any(y => y.SuspenseDate >= DbFunctions.TruncateTime(searchModel.SuspenseDateStart.Value)));
                }
                if (searchModel.SuspenseDateEnd.HasValue) {
                    query = query.Where(x => x.SuspenseOrderDetails.Any(y => y.SuspenseDate <= DbFunctions.TruncateTime(searchModel.SuspenseDateEnd.Value)));
                }

                searchModel.Records = query.Count();
                pageIndex = (pageIndex - 1) * this._pageSize > searchModel.Records ? 1 : pageIndex;

                return query.OrderByDescending(x => x.CreatedDatetime).Skip((pageIndex - 1) * this._pageSize).Take(this._pageSize).ToList();
            }
        }

        public List<SuspenseOrder> FetchQualifiedSuspenseOrderList() {
            using (var ctx = this.Context) {
                var query = from a in ctx.SuspenseOrders
                            join b in ctx.QualityOrders on a.SuspenseOrderId equals b.SuspenseOrderId
                            where b.Status == 2
                            select a;

                return query
                    .Include("SuspenseOrderDetails")
                    .Include("ProcurementOrder")
                    .OrderByDescending(x => x.CreatedDatetime).ToList();
            }
        }

        public List<SuspenseOrderDetail> FetchSuspenseOrderDetailList(int procurementOrderId) {
            using (var ctx = this.Context) {
                var query = from a in ctx.ProcurementOrders.Where(x => x.ProcurementOrderId == procurementOrderId)
                            join b in ctx.SuspenseOrders on a.ProcurementOrderId equals b.ProcurementOrderId
                            join c in ctx.SuspenseOrderDetails on b.SuspenseOrderId equals c.SuspenseOrderId
                            join d in ctx.QualityOrders on b.SuspenseOrderId equals d.SuspenseOrderId into dOutput
                            from d1 in dOutput.Where(x => x.Status == 2).DefaultIfEmpty()
                            //join e in ctx.QualityOrderDetails on new {
                            //    QualityOrderId = d1.QualityOrderId,
                            //    MaterialId = c.MaterialId
                            //} equals new {
                            //    QualityOrderId = e.QualityOrderId,
                            //    MaterialId = e.MaterialId
                            //} into eOutput
                            join e in ctx.QualityOrderDetails on d1.QualityOrderId equals e.QualityOrderId into eOutput
                            from e1 in eOutput.Where(x => x.MaterialId == c.MaterialId).DefaultIfEmpty()
                            select new { c, e1 };

                List<SuspenseOrderDetail> resultList = new List<SuspenseOrderDetail>();
                query.ToList().ForEach(x => {
                    x.c.SuspenseQuantity -= (x.e1 ?? new QualityOrderDetail()).UnQualifiedQuantity;
                    resultList.Add(x.c);
                });

                return resultList;
            }
        }

        public int GetTheMaxFlowNumber(string startString) {
            using (var ctx = this.Context) {
                SuspenseOrder suspenseOrder = ctx.SuspenseOrders
                    .Where(x => x.Code.StartsWith(startString))
                    .OrderByDescending(x => x.Code)
                    .FirstOrDefault();
                int maxFlowNumber = suspenseOrder == null
                    ? 0
                    : Convert.ToInt32(suspenseOrder.Code.Substring(suspenseOrder.Code.Length - 2, 2));
                return Math.Max(ctx.SuspenseOrders.Count(x => x.Code.StartsWith(startString)), maxFlowNumber);
            }
        }
    }
}
